unit UMono;

interface

uses classes, sysutils, variants, UMonoIntf, dialogs, RTTI, TypInfo, StrUtils,
  Generics.Collections;

const
  DefaultProxyNameSpace = 'Mono4Delphi';
  DefaultProxyClassName = 'TProxyAnyIntf';
type
  TMonoParamList = array of Pointer;
  TSignatureItem = array of string;
  TSignatureList = array of TSignatureItem;

  TMonoParamDef = packed record
    ParamType: TMonoTypeEnum;
    ParamTypeName: string;
    ByRef: boolean;
    monoType: PMonoType;
  end;

  TArrMonoParamDef = array of TMonoParamDef;

  TMonoMethodInfo = packed record
    signature: string;
    paramCount: integer;
    params: TArrMonoParamDef;
    result: TMonoParamDef;
  end;

  TAppDomain = class;

  TMonoJit = class(TObject)
  private
    //Fdomain: PMonoDomain;
    //Fassembly: PMonoAssembly;
    FFile: string;
    //FImage: PMonoImage;
    FMapD2M: TDictionary<TClass, string>;
    FMapM2D: TDictionary<string, TClass>;
    FProxyNameSpace: string;
    FProxyClassName: string;
    FVersion: UTF8String;
    FMainApp: TAppDomain;
    function getassembly: PMonoAssembly;
    function getdomain: PMonoDomain;
    function getImage: PMonoImage;
  public
    constructor Create(aVersion, aFile, aDebugAddress: string; aDebugPort:integer);
    procedure BeforeDestruction; override;
    procedure AddInternalCall(const aNameSpace, aClassName,
      aMethodName: string; aCall: Pointer);
    procedure LoadAssembly;
    function Exec: integer;
    property assembly: PMonoAssembly read getassembly;
    property AppFile: string read FFile;
    property Image: PMonoImage read getImage;
    property Domain: PMonoDomain read getdomain;

    function MonoExceptionMessage(aExceptionObj: PMonoObject): string;
    procedure GC_Collect;
    function GetMonoMethodInfo(aMethod: PMonoMethod): TMonoMethodInfo;
    function FindMonoMethod(monoclass: PMonoClass; aName: string;
      const aSignature: string; aTestSignature: boolean = true): PMonoMethod; overload;
    function FindMonoMethod(monoclass: PMonoClass; aName: string;
      const aSignature: TSignatureList; aTestSignature: boolean = true): PMonoMethod; overload;
    function FindMonoProperty(monoclass: PMonoClass; aName: string): PMonoProperty;
    function FindMonoField(monoclass: PMonoClass; aName: string): PMonoClassField;
    function FindMonoClass(aNameSpace, aClassName: string; aImage: PMonoImage = nil): PMonoClass;

    function CreateMonoObject(monoclass: PMonoClass; aDomain: PMonoDomain = nil): PMonoObject; overload;
    function CreateMonoObject(monoclass: PMonoClass; const aSignature: string;
      const aParams: array of const; aDomain: PMonoDomain = nil): PMonoObject; overload;
    function CreateMonoProxy(aObject: TObject; aMonoTypeName: string = ''; aDomain: PMonoDomain = nil): PMonoObject;
    function CallMonoMethod(aMethod: PMonoMethod; aThis: PMonoObject;
      const aParams: array of const; aDomain: PMonoDomain = nil ): PMonoObject;
    procedure GenSignatureList(Arguments: TVarDataArray; out aList: TSignatureList);
    procedure CallMonoMethodVar(aMethod: PMonoMethod; aThis: PMonoObject;
      const Arguments: TVarDataArray; var Dest: TVarData; aDomain: PMonoDomain = nil);
    function CallMonoMethodVirt(aMethod: PMonoMethod; aThis: PMonoObject;
      const aParams: array of const; aDomain: PMonoDomain = nil ): PMonoObject;
    procedure GetMonoClassInterfaces(monoclass: PMonoClass; aList: TList);
    procedure AddClassMap(aDelphiClass: TClass; aMonoIntf: string);
    function GetClassIntf(aDelphiClass: TClass; aBaseMonoIntf: string): string;

    procedure GetClassMethodText(aKlass: PMonoClass; aInfo: TStrings);
    procedure GetClassFieldText(aKlass: PMonoClass; aInfo: TStrings);
    property MainApp: TAppDomain read FMainApp write FMainApp;
  end;


  TAppDomain = class(TObject)
  private
    FInitDom: boolean;
    Fdomain: PMonoDomain;
    Fassembly: PMonoAssembly;
    FImage: PMonoImage;
    FProxyNameSpace: string;
    FProxyClassName: string;
  public
    constructor Create(aName: string; aMonoJit: TMonoJit; aInit: boolean = false);
    destructor Destroy; override;
    procedure LoadMainAssembly(aFileName: string);
    function AddAssembly(aFileName: string): PMonoAssembly;

    property MonoDomain: PMonoDomain read FDomain;
    property MainAssembly: PMonoAssembly read FAssembly;
    property MainImage: PMonoImage read FImage;
    property ProxyNameSpace: string read FProxyNameSpace write FProxyNameSpace;
    property ProxyClassName: string read FProxyClassName write FProxyClassName;
  end;


  TRecArgTyp = (atNull, atPointer, atUInt8, atInt8, atUInt16, atInt16, atUInt32, atInt32,
     atUInt64, atInt64, atSingle, atDouble, atString, atBool, atObject, atArray, atChar, atDateTime);

  TRecArgInfo = record
    MonoType: PMonoType;
    _Dispose: boolean;
    ArgTyp: TRecArgTyp;
    constructor Create(aMonoType: PMonoType; aDispose: boolean; aArgTyp: TRecArgTyp);
  end;

  TMonoInvoke = class(TObject)
  private
    FDomain: PMonoDomain;
    FArgs: TList;
    FArgTypes: TList<TRecArgInfo>;
    FResult: PMonoObject;
    FMonoException: PMonoObject;
  public
    procedure AddPtrArg(aValue: Pointer; aMonoType: PMonoType; aDispose: boolean; aArgTyp: TRecArgTyp);
    procedure AddIntArg(aValue: integer; aMonoType: PMonoType);
    procedure AddStrArg(aValue: widestring; aMonoType: PMonoType);
    procedure AddBoolArg(aValue: wordbool; aMonoType: PMonoType);
    procedure AddDoubleArg(aValue: double; aMonoType: PMonoType);
    procedure AddObjectArg(aValue: TObject; aMonoType: PMonoType);

    procedure AddUInt8Arg(aValue: Byte; aMonoType: PMonoType);
    procedure AddInt8Arg(aValue: ShortInt; aMonoType: PMonoType);
    procedure AddUInt16Arg(aValue: Word; aMonoType: PMonoType);
    procedure AddInt16Arg(aValue: SmallInt; aMonoType: PMonoType);
    procedure AddUIntArg(aValue: Cardinal; aMonoType: PMonoType);
    procedure AddUInt64Arg(aValue: UInt64; aMonoType: PMonoType);
    procedure AddInt64Arg(aValue: int64; aMonoType: PMonoType);
    procedure AddSingleArg(aValue: single; aMonoType: PMonoType);
    procedure AddCharArg(aValue: char; aMonoType: PMonoType);
    procedure AddDateTimeArg(aValue: TDateTime; aMonoType: PMonoType);

    procedure AddVarRec(aMonoType: PMonoType; aValue: TVarRec);
    procedure SetVarRec(aIndex: integer; aValue: TVarRec);

    procedure AddVarData(aMonoType: PMonoType; aValue: TVarData);
    procedure SetVarData(aIndex: integer; var aValue: TVarData);
    procedure SetResultVarData(aMonoType: PMonoType; aObjResult: PMonoObject; var aResult: TVarData);

    procedure ClearArgs;

    function ResultAsInteger: integer;
    function ResultAsString: string;
    function ResultAsBoolean: wordbool;
    function ResultAsDouble: double;

    function MonoInvoke(aObject: PMonoObject; aMethod: PMonoMethod): boolean;
    function MonoInvokeByName(aObject: PMonoObject; aMethodName: string): boolean; overload;
    function MonoInvokeByName(aClass: PMonoClass; aMethodName: string): boolean; overload;
    function MonoInvokeVirtual(aClass: PMonoClass; aObject: PMonoObject; aMethodName: string): boolean;

    constructor Create(aDomain: PMonoDomain);
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;

    property MonoException: PMonoObject read FMonoException;
    property ResultObj: PMonoObject read FResult;

  end;

  TDateTime_ToOADate = function (this: PMonoObject; var exc:PMonoObject): TDateTime; stdcall;
  TDateTime_FromOADate = function (value: TDateTime; var exc:PMonoObject): PMonoObject; stdcall;
  TDateTime_FromBinary = function (value: Int64; var exc:PMonoObject): PMonoObject; stdcall;
  TDateTime_ToBinary = function (this: PMonoObject; var exc:PMonoObject): Int64; stdcall;

  TMonoConvert = class(TObject)
  private
    class var FToOADate: TDateTime_ToOADate;
    class var FFromOADate: TDateTime_FromOADate;
    class var FDateToBin: TDateTime_ToBinary;
    class var FDateFromBin: TDateTime_FromBinary;
    class var FDateTimeType: PMonoType;
    class var FDateTimeTypeRef: PMonoType;
    class function getDateTimeType: PMonoType; static;
    class function getDateTimeTypeRef: PMonoType; static;
  public
    class function StringToMonoStr(Value: String; aDomain: PMonoDomain = nil): PMonoString;
    class function MonoStrToString(Value: PMonoString): String;

    class function SmallIntToMonoObj(Value: smallint; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToSmallInt(Value: PMonoObject): smallint;
    class function IntToMonoObj(Value: integer; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToInt(Value: PMonoObject): integer;
    class function Int64ToMonoObj(Value: int64; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToInt64(Value: PMonoObject): int64;
    class function DoubleToMonoObj(Value: double; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToDouble(Value: PMonoObject): double;
    class function BooleanToMonoObj(Value: wordbool; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToBoolean(Value: PMonoObject): wordbool;
    class function SingleToMonoObj(Value: single; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToSingle(Value: PMonoObject): single;

    class function UIntToMonoObj(Value: cardinal; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToUInt(Value: PMonoObject): cardinal;
    class function Int16ToMonoObj(Value: smallint; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToInt16(Value: PMonoObject): smallint;
    class function UInt16ToMonoObj(Value: word; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToUInt16(Value: PMonoObject): word;
    class function Int8ToMonoObj(Value: shortint; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToInt8(Value: PMonoObject): shortint;
    class function UInt8ToMonoObj(Value: byte; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToUInt8(Value: PMonoObject): byte;
    class function UInt64ToMonoObj(Value: UInt64; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToUInt64(Value: PMonoObject): UInt64;


    class property DateTimeType: PMonoType read getDateTimeType;
    class property DateTimeTypeRef: PMonoType read getDateTimeTypeRef;
    class function MonoObjToDateTime(Value: PMonoObject): TDateTime;
    class function DateTimeToMonoObj(Value: TDateTime; aDomain: PMonoDomain = nil): PMonoObject;
    class function DateTimeToInt64(value: TDateTime; aDomain: PMonoDomain = nil): Int64;
    class function Int64ToDateTime(value: Int64): TDateTime;

    class function PointerToMonoObj(Value: Pointer; aDomain: PMonoDomain = nil): PMonoObject;
    class function MonoObjToPointer(Value: PMonoObject): Pointer;

    class procedure MonoArrayToVariant(aArray: PMonoArray; aMonoType: PMonoType; var aResult: TVarData);
    class function VariantToMonoArray(aMonoType: PMonoType; const aValue: TVarData; aDomain: PMonoDomain = nil): PMonoArray;

    class procedure MonoArrayToDynArray(aArray: PMonoArray; aMonoType: PMonoType; var aResult: TValue; ArrayTypeInfo: PTypeInfo);
    class function DynArrayToMonoArray(aMonoType: PMonoType; const aValue: TValue; ArrayTypeInfo: PTypeInfo; aDomain: PMonoDomain = nil): PMonoArray;
  end;

  function ExtractNameSpace(aTypeName: string): string;
  function ExcludeNameSpace(aTypeName: string): string;
  function MonoTypeEnumToString(te: TMonoTypeEnum): string;
  function MonoEnumToSimpleString(te: TMonoTypeEnum; aDefault: string): string;

var
  MonoJit: TMonoJit;

implementation

uses math, UVarMono;

function ExtractNameSpace(aTypeName: string): string;
var
  i: integer;
begin
  i := pos('.', aTypeName);
  if i > 0 then
  begin
    result := Copy(aTypeName, 1, i-1);
  end
  else
    result := '';
end;

function ExcludeNameSpace(aTypeName: string): string;
var
  i: integer;
begin
  i := pos('.', aTypeName);
  if i > 0 then
  begin
    result := Copy(aTypeName, i+1, length(aTypename));
  end
  else
    result := aTypeName;
end;

function MonoTypeEnumToString(te: TMonoTypeEnum): string;
begin
  case te of
    MONO_TYPE_END:
      result := 'MONO_TYPE_END';
    MONO_TYPE_VOID:
      result := 'MONO_TYPE_VOID';
    MONO_TYPE_BOOLEAN:
      result := 'MONO_TYPE_BOOLEAN';
    MONO_TYPE_CHAR:
      result := 'MONO_TYPE_CHAR';
    MONO_TYPE_I1:
      result := 'MONO_TYPE_I1';
    MONO_TYPE_U1:
      result := 'MONO_TYPE_U1';
    MONO_TYPE_I2:
      result := 'MONO_TYPE_I2';
    MONO_TYPE_U2:
      result := 'MONO_TYPE_U2';
    MONO_TYPE_I4:
      result := 'MONO_TYPE_I4';
    MONO_TYPE_U4:
      result := 'MONO_TYPE_U4';
    MONO_TYPE_I8:
      result := 'MONO_TYPE_I8';
    MONO_TYPE_U8:
      result := 'MONO_TYPE_U8';
    MONO_TYPE_R4:
      result := 'MONO_TYPE_R4';
    MONO_TYPE_R8:
      result := 'MONO_TYPE_R8';
    MONO_TYPE_STRING:
      result := 'MONO_TYPE_STRING';
    MONO_TYPE_PTR:
      result := 'MONO_TYPE_PTR';
    MONO_TYPE_BYREF:
      result := 'MONO_TYPE_BYREF';
    MONO_TYPE_VALUETYPE:
      result := 'MONO_TYPE_VALUETYPE';
    MONO_TYPE_CLASS:
      result := 'MONO_TYPE_CLASS';
    MONO_TYPE_VAR:
      result := 'MONO_TYPE_VAR';
    MONO_TYPE_ARRAY:
      result := 'MONO_TYPE_ARRAY';
    MONO_TYPE_GENERICINST:
      result := 'MONO_TYPE_GENERICINST';
    MONO_TYPE_TYPEDBYREF:
      result := 'MONO_TYPE_TYPEDBYREF';
    MONO_TYPE_I:
      result := 'MONO_TYPE_I';
    MONO_TYPE_U:
      result := 'MONO_TYPE_U';
    MONO_TYPE_FNPTR:
      result := 'MONO_TYPE_FNPTR';
    MONO_TYPE_OBJECT:
      result := 'MONO_TYPE_OBJECT';
    MONO_TYPE_SZARRAY:
      result := 'MONO_TYPE_SZARRAY';
    MONO_TYPE_MVAR:
      result := 'MONO_TYPE_MVAR';
    MONO_TYPE_CMOD_REQD:
      result := 'MONO_TYPE_CMOD_REQD';
    MONO_TYPE_CMOD_OPT:
      result := 'MONO_TYPE_CMOD_OPT';
    MONO_TYPE_INTERNAL:
      result := 'MONO_TYPE_INTERNAL';
    MONO_TYPE_MODIFIER:
      result := 'MONO_TYPE_MODIFIER';
    MONO_TYPE_SENTINEL:
      result := 'MONO_TYPE_SENTINEL';
    MONO_TYPE_PINNED:
      result := 'MONO_TYPE_PINNED';
  else
    result := '<?>';
  end;
end;

function MonoEnumToSimpleString(te: TMonoTypeEnum; aDefault: string): string;
begin
  case te of
    MONO_TYPE_BOOLEAN:
      result := 'bool';
    MONO_TYPE_CHAR:
      result := 'char';
    MONO_TYPE_I1:
      result := 'sbyte';
    MONO_TYPE_U1:
      result := 'byte';
    MONO_TYPE_I2:
      result := 'int16';
    MONO_TYPE_U2:
      result := 'uint16';
    MONO_TYPE_I4:
      result := 'int';
    MONO_TYPE_U4:
      result := 'uint';
    MONO_TYPE_I8:
      result := 'long';
    MONO_TYPE_U8:
      result := 'ulong';
    MONO_TYPE_R4:
      result := 'single';
    MONO_TYPE_R8:
      result := 'double';
    MONO_TYPE_STRING:
      result := 'string';
    MONO_TYPE_OBJECT:
      result := 'object';
  else
    result := aDefault;
  end;
end;

{ TMonoJit }

procedure TMonoJit.AddClassMap(aDelphiClass: TClass; aMonoIntf: string);
begin
  FMapD2M.AddOrSetValue(aDelphiClass, aMonoIntf);
  FMapM2D.AddOrSetValue(aMonoIntf, aDelphiClass);
end;

procedure TMonoJit.AddInternalCall(const aNameSpace, aClassName,
  aMethodName: string; aCall: Pointer);
var
  s: UTF8String;
begin
  if aNameSpace <> '' then
    s := UTF8Encode(format('%s.%s::%s', [aNameSpace, aClassName, aMethodName]))
  else
    s := UTF8Encode(format('%s::%s', [aClassName, aMethodName]));

  mono_add_internal_call(PAnsiChar(s), aCall);
end;

procedure TMonoJit.BeforeDestruction;
begin
  inherited;
  FMapD2M.Free;
  FMapM2D.Free;

  if Assigned(FMainApp) then
  begin
     mono_gc_collect (mono_gc_max_generation ());
     mono_jit_cleanup (FMainApp.MonoDomain);
     //Fdomain := nil;
     FMainApp.Free;
  end;

end;

function TMonoJit.CallMonoMethod(aMethod: PMonoMethod; aThis: PMonoObject;
  const aParams: array of const; aDomain: PMonoDomain = nil ): PMonoObject;
var
  sig: PMonoMethodSignature;
  iter2: Pointer;
  t: PMonoType;
  lList: TList;
  i: integer;
  args: TMonoInvoke;
  lexception: PMonoObject;
begin
  if aDomain = nil then
    if aThis <> nil then
      aDomain := mono_object_get_domain(aThis)
    else
      aDomain := mono_domain_get();

  sig := mono_method_signature(aMethod);
  iter2 := nil;
  t := mono_signature_get_params(sig, iter2);
  lList := TList.Create;
  args := TMonoInvoke.Create(aDomain);
  try
    while t <> nil do
    begin
      lList.Add(t);
      t := mono_signature_get_params(sig, iter2);
    end;

    if lList.Count = 0 then
      args.AddPtrArg(nil, mono_class_get_type(mono_get_void_class), false, atNull);

    for i := 0 to lList.Count - 1 do
    begin
      args.AddVarRec(PMonoType(lList[i]), aParams[i]);
    end;
    lexception := nil;
    result := mono_runtime_invoke(aMethod, PPointer(aThis), args.FArgs.List^[0], lexception);
    if (lexception <> nil) then
    begin
      raise Exception.Create(MonoExceptionMessage(lexception));
    end;
    // ref args
    for i := 0 to lList.Count - 1 do
    begin
      args.SetVarRec(i, aParams[i]);
    end;

  finally
    lList.Free;
    args.Free;
  end;
end;

procedure TMonoJit.CallMonoMethodVar(aMethod: PMonoMethod; aThis: PMonoObject;
  const Arguments: TVarDataArray; var Dest: TVarData; aDomain: PMonoDomain = nil);
var
  sig: PMonoMethodSignature;
  iter2: Pointer;
  t: PMonoType;
  lList: TList;
  i: integer;
  args: TMonoInvoke;
  lexception: PMonoObject;
  lresult: PMonoObject;
begin
  if aDomain = nil then
    if aThis <> nil then
      aDomain := mono_object_get_domain(aThis)
    else
      aDomain := mono_domain_get();
  sig := mono_method_signature(aMethod);
  iter2 := nil;
  t := mono_signature_get_params(sig, iter2);
  lList := TList.Create;
  args := TMonoInvoke.Create(aDomain);
  try
    while t <> nil do
    begin
      lList.Add(t);
      t := mono_signature_get_params(sig, iter2);
    end;

    if llist.Count <> Length(Arguments) then
      if (llist.Count = 0) and (Length(Arguments) = 1) then
      begin
        if VarType(variant(Arguments[0])) <> varError then
         raise Exception.Create('Error ArgCount');
      end
      else
        raise Exception.Create('Error ArgCount');


    if lList.Count = 0 then
      args.AddPtrArg(nil, mono_class_get_type(mono_get_void_class), false, atNull);


    for i := 0 to lList.Count - 1 do
    begin
      args.AddVarData(PMonoType(lList[i]), Arguments[i]);
    end;
    lexception := nil;
    lresult := mono_runtime_invoke(aMethod, PPointer(aThis), args.FArgs.List^[0], lexception);
    if (lexception <> nil) then
    begin
      raise Exception.Create(MonoExceptionMessage(lexception));
    end;
    // restore ref params - tipi non compatibili
    for i := 0 to lList.Count - 1 do
    begin
      args.SetVarData(i, Arguments[i]);
    end;

    t := mono_signature_get_return_type(sig);
    args.SetResultVarData(t, lresult, Dest);

  finally
    lList.Free;
    args.Free;
  end;
end;

function TMonoJit.CallMonoMethodVirt(aMethod: PMonoMethod; aThis: PMonoObject;
  const aParams: array of const; aDomain: PMonoDomain = nil): PMonoObject;
var
  m: PMonoMethod;
begin
  m := mono_object_get_virtual_method(aThis, aMethod);
  result := CallMonoMethod(m, athis, aParams, aDomain);
end;

constructor TMonoJit.Create(aVersion, aFile, aDebugAddress: string; aDebugPort:integer);
var
  sDebug: string;
  sOpt, lVersion: UTF8String;
begin
  inherited Create;
  FProxyNameSpace := 'Mono4Delphi';
  FProxyClassName := 'TProxyAnyIntf';

  FMapD2M := TDictionary<TClass, string>.Create();
  FMapM2D := TDictionary<string, TClass>.Create();


  sDebug := GetEnvironmentVariable('MONODEV_SOFT_DEBUG');
  if (sDebug = '')and(aDebugAddress <> '')and(aDebugPort <> 0) then
    sDebug := format('transport=dt_socket,address=%s:%d', [aDebugAddress, aDebugPort]);

  if sDebug <> '' then
  begin
    //ShowMessage('debug');
    sOpt := UTF8Encode(format('--debugger-agent=%s', [sDebug]));
    mono_jit_parse_options(1, [PAnsiChar(sOpt)]);
    mono_debug_init(MONO_DEBUG_FORMAT_MONO);
  end;


  if aVersion = '' then
    aVersion := 'v4.0.30319';
  lVersion := UTF8Encode(aVersion);
  FVersion := lVersion;
  //Fdomain := mono_jit_init_version('DelphiDomain', PAnsiChar(lVersion));

(*        domain.c
static const MonoRuntimeInfo supported_runtimes[] = {
	{"v2.0.50215","2.0", { {2,0,0,0},    {8,0,0,0}, { 3, 5, 0, 0 } }	},
	{"v2.0.50727","2.0", { {2,0,0,0},    {8,0,0,0}, { 3, 5, 0, 0 } }	},
	{"v4.0.20506","4.0", { {4,0,0,0},    {10,0,0,0}, { 4, 0, 0, 0 } }   },
	{"v4.0.30128","4.0", { {4,0,0,0},    {10,0,0,0}, { 4, 0, 0, 0 } }   },
	{"v4.0.30319","4.0", { {4,0,0,0},    {10,0,0,0}, { 4, 0, 0, 0 } }   },
	{"moonlight", "2.1", { {2,0,5,0},    {9,0,0,0}, { 3, 5, 0, 0 } }    },
};
*)
  FFile := aFile;
  FMainApp := TAppDomain.Create('DelphiDomain', self, true);
end;

function TMonoJit.CreateMonoObject(monoclass: PMonoClass; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_object_new(aDomain, monoclass);
  // default constructor
  mono_runtime_object_init(result);
end;

function TMonoJit.Exec: integer;
var
  param1: UTF8String;
  ap: array [0 .. 1] of PAnsiChar;
begin
  param1 := UTF8Encode(ExtractFileName(FFile));
  ap[0] := PAnsiChar(param1);
  ap[1] := nil;
  result := mono_jit_exec(FMainApp.MonoDomain, FMainApp.MainAssembly, 1, ap);
  param1 := '';
end;


procedure AssemblyItem(data: Pointer; user_data: Pointer); cdecl;
begin
  Tlist(user_data).Add(data);
  //ShowMessage(mono_image_get_filename(mono_assembly_get_image(PMonoAssembly(data))));
end;

function TMonoJit.FindMonoClass(aNameSpace, aClassName: string; aImage: PMonoImage): PMonoClass;
var
  lNameSpace, lClassName: UTF8String;
  lsAssembly: TList;
  p: Pointer;
begin
  if aImage = nil then
      aImage := FMainApp.MainImage;
  lNameSpace := UTF8Encode(aNameSpace);
  lClassName := UTF8Encode(aClassName);
  result := mono_class_from_name(aImage, PAnsiChar(lNameSpace),
    PAnsiChar(lClassName));

  if result = nil then
  begin
    lsAssembly := TList.Create;
    try
      mono_assembly_foreach(AssemblyItem, Pointer(lsAssembly));
      for p in lsAssembly do
      begin
        result := mono_class_from_name(mono_assembly_get_image(PMonoAssembly(p)), PAnsiChar(lNameSpace), PAnsiChar(lClassName));
        if result <> nil then
          break;
      end;
    finally
      lsAssembly.Free;
    end;
  end;
end;

function TMonoJit.FindMonoField(monoclass: PMonoClass;
  aName: string): PMonoClassField;
var
  iter: Pointer;
  lKlass: PMonoClass;
  f: PMonoClassField;
  s: string;
begin
  result := nil;
  iter := nil;
  lKlass := monoclass;
  while lKlass <> nil do
  begin
    f := mono_class_get_fields(lKlass, iter);
    s := UTF8ToUnicodeString(mono_field_get_name(f));
    if sametext(s, aName) then
    begin
      result := f;
      exit;
    end;
    lKlass := mono_class_get_parent(lKlass);
  end;
end;

function TMonoJit.FindMonoMethod(monoclass: PMonoClass; aName: string;
  const aSignature: TSignatureList; aTestSignature: boolean): PMonoMethod;
var
  iter: Pointer;
  m: PMonoMethod;
  s: string;
  lKlass: PMonoClass;
  lInfo: TMonoMethodInfo;

  function TestSignature: boolean;
  var
    sl: TStringList;
    i: integer;
  begin
    result := true;
    sl := TStringList.Create;
    try
      sl.Text := ReplaceStr(lInfo.signature, ',', #13#10);
      for i := 0 to sl.Count-1 do
      begin
        result := MatchStr(sl[i], aSignature[i]);
        if not result then
        begin
          result := MatchStr('?', aSignature[i]);
          if not result then
          exit;
        end;
      end;
    finally
      sl.Free;
    end;
  end;

begin
  result := nil;
  lKlass := monoclass;
  while lKlass <> nil do
  begin
    iter := nil;
    m := mono_class_get_methods(lKlass, iter);
    while m <> nil do
    begin
      s := UTF8ToUnicodeString(mono_method_get_name(m));
      if sametext(s, aName) then
      begin
        if aTestSignature then
        begin
          lInfo := GetMonoMethodInfo(m);
          if (lInfo.paramCount = 0) and (Length(aSignature) = 0) then
          begin
            result := m;
            exit;
          end;
          //if sametext(lInfo.signature, aSignature) then
          if TestSignature then
          begin
            result := m;
            exit;
          end;
        end
        else begin
          result := m;
          exit;
        end;
      end;
      m := mono_class_get_methods(lKlass, iter);
    end;

    lKlass := mono_class_get_parent(lKlass);
  end;
end;

function TMonoJit.FindMonoMethod(monoclass: PMonoClass; aName: string;
  const aSignature: string; aTestSignature: boolean = true): PMonoMethod;
var
  iter: Pointer;
  m: PMonoMethod;
  s: string;
  lKlass: PMonoClass;
  lInfo: TMonoMethodInfo;
begin
  result := nil;
  lKlass := monoclass;

  while lKlass <> nil do
  begin
    iter := nil;
    m := mono_class_get_methods(lKlass, iter);
    while m <> nil do
    begin
      s := UTF8ToUnicodeString( mono_method_get_name(m));
      if sametext(s, aName) then
      begin
        if not aTestSignature then
        begin
          result := m;
          exit;
        end;
        lInfo := GetMonoMethodInfo(m);
        if (lInfo.paramCount = 0) and (aSignature = '') then
        begin
          result := m;
          exit;
        end;
        if sametext(lInfo.signature, aSignature) then
        begin
          result := m;
          exit;
        end;
      end;
      m := mono_class_get_methods(lKlass, iter);
    end;

    lKlass := mono_class_get_parent(lKlass);
  end;
end;

function TMonoJit.FindMonoProperty(monoclass: PMonoClass;
  aName: string): PMonoProperty;
var
  iter: Pointer;
  lKlass: PMonoClass;
  mp: PMonoProperty;
  s: string;
begin
  lKlass := monoclass;
  while lKlass <> nil do
  begin
    iter := nil;
    mp := mono_class_get_properties(lKlass, iter);
    while mp <> nil do
    begin
      s := UTF8ToUnicodeString( mono_property_get_name(mp));
      if sametext(s, aName) then
      begin
        result := mp;
        exit;
      end;
      mp := mono_class_get_properties(lKlass, iter);
    end;
    lKlass := mono_class_get_parent(lKlass);
  end;
  result := nil;
end;

procedure TMonoJit.GC_Collect;
begin
  mono_gc_collect (mono_gc_max_generation ());
end;

procedure TMonoJit.GenSignatureList(Arguments: TVarDataArray;
  out aList: TSignatureList);
var
  i: integer;
  //p: PVarData;
  isByRef: boolean;
begin
  if Length(Arguments) = 1 then
    if (Arguments[0].VType and varTypeMask) = varError then
    begin
      aList := nil;
      exit;
    end;

  setLength(aList, Length(Arguments));


  for i := 0 to Length(Arguments) - 1 do
  begin
    isByRef := VarIsByRef(variant(Arguments[i]));

    case (Arguments[i].VType and varTypeMask) of
      varVariant: begin
        //p := Arguments[i].VPointer;
        // todo: test p^
        aList[i] := TSignatureItem.Create('?');
      end;
      varArray: begin
        // +[]
        aList[i] := TSignatureItem.Create('?');
      end;
      varOleStr: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('string', 'string&')
        else
          aList[i] := TSignatureItem.Create('string');
      end;
      varByte: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('byte', 'uint16', 'uint', 'ulong', 'byte&')
        else
          aList[i] := TSignatureItem.Create('byte', 'uint16', 'uint', 'ulong', 'System.Nullable`1<int>');
      end;
      varShortInt: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('sbyte', 'int16', 'int', 'long', 'sbyte&')
        else
          aList[i] := TSignatureItem.Create('sbyte', 'int16', 'int', 'long', 'System.Nullable`1<int>');
      end;
      varWord: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('uint16', 'uint', 'ulong', 'uint16&')
        else
          aList[i] := TSignatureItem.Create('uint16', 'uint', 'ulong', 'System.Nullable`1<int>');
      end;
      varSmallint: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('int16', 'int', 'long', 'int16&')
        else
          aList[i] := TSignatureItem.Create('int16', 'int', 'long', 'System.Nullable`1<int>');
      end;
      varInteger: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('int', 'long', 'int&')
        else
          aList[i] := TSignatureItem.Create('int', 'long', 'System.Nullable`1<int>');
      end;
      varLongWord: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('uint', 'ulong', 'uint&')
        else
          aList[i] := TSignatureItem.Create('uint', 'ulong', 'System.Nullable`1<int>');
      end;
      varInt64: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('long', 'long&')
        else
          aList[i] := TSignatureItem.Create('long', 'System.Nullable`1<long>');
      end;
      varUInt64: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('ulong', 'ulong&')
        else
          aList[i] := TSignatureItem.Create('ulong');
      end;
      varSingle: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('single', 'double', 'single&')
        else
          aList[i] := TSignatureItem.Create('single', 'double');
      end;
      varDouble, varCurrency: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('double', 'double&')
        else
          aList[i] := TSignatureItem.Create('double');
      end;
      varBoolean: begin
        if isByRef then
          aList[i] := TSignatureItem.Create('bool', 'bool&')
        else
          aList[i] := TSignatureItem.Create('bool');
      end
    else
      aList[i] := TSignatureItem.Create('?');
    end;
  end;

end;

function TMonoJit.getassembly: PMonoAssembly;
begin
  Result := FMainApp.MainAssembly;
end;

procedure TMonoJit.GetClassFieldText(aKlass: PMonoClass; aInfo: TStrings);
var
  iter: Pointer;
  f: PMonoClassField;
  t: PMonoType;
  s: string;
  lParentKlass: PMonoClass;
  flags: cardinal;
begin
  aInfo.BeginUpdate;
  try
    iter := nil;
    f := mono_class_get_fields(aKlass, iter);
    while f <> nil do
    begin
      s := UTF8ToString(mono_field_get_name(f));
      t := mono_field_get_type(f);
      s := s + ' : '+ UTF8ToString(mono_type_get_name(t));
      flags := mono_field_get_flags(f);
      if (flags and FIELD_ATTRIBUTE_STATIC) = FIELD_ATTRIBUTE_STATIC then
      begin
        s := 'const '+s;
      end
      else begin
        s := 'var '+ s;
        s := s + ' offset: '+intTostr(mono_field_get_offset(f));
      end;

      aInfo.Add(s);
      f := mono_class_get_fields(aklass, iter);
    end;
    lParentKlass := mono_class_get_parent(aklass);
    if lParentKlass <> nil then
      GetClassFieldText(lParentKlass, aInfo);
  finally
    aInfo.EndUpdate;
  end;
end;

function TMonoJit.GetClassIntf(aDelphiClass: TClass;
  aBaseMonoIntf: string): string;
var
  lBaseClass, lClass: TClass;
begin
  if not FMapM2D.TryGetValue(aBaseMonoIntf, lBaseClass) then
    lBaseClass := TObject;
  result := '';
  lClass := aDelphiClass;
  while lClass <> nil do
  begin
    if FMapD2M.TryGetValue(lClass, result) then
    begin
      break;
    end;
    lClass := lClass.ClassParent;
  end;

  if lClass = nil then
    raise Exception.Create('ClassType not found');

  if (lClass <> lBaseClass) and (not lClass.InheritsFrom(lBaseClass)) then
    raise Exception.Create('Invalid ClassType Result');

end;

procedure TMonoJit.GetClassMethodText(aKlass: PMonoClass; aInfo: TStrings);
var
  iter: Pointer;
  m: PMonoMethod;
  sig: PMonoMethodSignature;
  s: string;
  //narg: cardinal;
  //varArgStart: integer;
  lParentKlass: PMonoClass;
  t: PMonoType;
  te: TMonoTypeEnum;
begin
  aInfo.BeginUpdate;
  try
    iter := nil;
    m := mono_class_get_methods(aklass, iter);
    while m <> nil do
    begin
      s := UTF8ToString(mono_method_get_name(m));
      sig := mono_method_signature(m);
      //narg := mono_signature_get_param_count(sig); // numero di parametri
      //varArgStart := mono_signature_vararg_start(sig);

      s := s + ' ('+UTF8ToString( mono_signature_get_desc(sig, true))+')';

      t := mono_signature_get_return_type(sig);
      if t <> nil then
      begin
        te := mono_type_get_type(t);
        if te = MONO_TYPE_VOID then
          s := 'procedure ' + s
        else
          s := 'function ' + s + ': ' + MonoEnumToSimpleString(te, UTF8ToString( mono_type_get_name(t)));
      end
      else
        s := 'procedure ' + s;

      if not mono_signature_is_instance(sig) then
        s := 'class ' + s;
      // mono_signature_vararg_start

      aInfo.Add(s);
      m := mono_class_get_methods(aklass, iter);
    end;
    lParentKlass := mono_class_get_parent(aklass);
    if lParentKlass <> nil then
      GetClassMethodText(lParentKlass, aInfo);
  finally
    aInfo.EndUpdate;
  end;
end;

function TMonoJit.getdomain: PMonoDomain;
begin
  Result := FMainApp.MonoDomain;
end;

function TMonoJit.getImage: PMonoImage;
begin
  Result := FMainApp.MainImage;
end;

procedure TMonoJit.GetMonoClassInterfaces(monoclass: PMonoClass; aList: TList);
var
  iter: Pointer;
  lKlass: PMonoClass;
begin
  iter := nil;
  lKlass := mono_class_get_interfaces(monoclass, iter);
  while lKlass <> nil do
  begin
    aList.Add(lKlass);
    lKlass := mono_class_get_interfaces(monoclass, iter);
  end;
end;

function TMonoJit.GetMonoMethodInfo(aMethod: PMonoMethod): TMonoMethodInfo;
var
  sig: PMonoMethodSignature;
  iter2: Pointer;
  t: PMonoType;
  lList: TList;
  i: integer;
begin
  sig := mono_method_signature(aMethod);
  result.signature := UTF8ToString( mono_signature_get_desc(sig, true));
  result.paramCount := mono_signature_get_param_count(sig);
  iter2 := nil;
  t := mono_signature_get_params(sig, iter2);
  lList := TList.Create;
  try
    while t <> nil do
    begin
      lList.Add(t);
      t := mono_signature_get_params(sig, iter2);
    end;
    setlength(result.params, lList.Count);
    for i := 0 to lList.Count - 1 do
    begin
      result.params[i].monoType := PMonoType(lList[i]);
      result.params[i].ParamType := mono_type_get_type(lList[i]);
      result.params[i].ParamTypeName := UTF8ToString( mono_type_get_name(lList[i]));
      result.params[i].ByRef := mono_type_is_byref(lList[i]);
    end;
  finally
    lList.Free;
  end;
  t := mono_signature_get_return_type(sig);
  result.result.ParamType := mono_type_get_type(t);
  result.result.ParamTypeName := UTF8ToString( mono_type_get_name(t));
  result.result.monoType := t;
end;

procedure TMonoJit.LoadAssembly;
//var
//  lFile: RawByteString;
begin
//  lFile := UTF8Encode(FFile);
//  Fassembly := mono_domain_assembly_open(Fdomain, PAnsiChar(lFile));
//  if Fassembly = nil then
//    raise Exception.Create('assembly non caricato');
//  FImage := mono_assembly_get_image(Fassembly);
  FMainApp.LoadMainAssembly(FFile);
end;

function TMonoJit.MonoExceptionMessage(aExceptionObj: PMonoObject): string;
var
  lClass: PMonoClass;
  m: PMonoMethod;
  lres: PMonoObject;
begin
  try
    lClass := mono_object_get_class(aExceptionObj);
    m := FindMonoMethod(lClass, 'get_Message', '');
    if m = nil then
      raise Exception.Create('');

    lres := CallMonoMethod(m, aExceptionObj, []);
    result := TMonoConvert.MonoStrToString(PMonoString(lres));

  except
    result := 'Mono Exception';
  end;
end;

function TMonoJit.CreateMonoObject(monoclass: PMonoClass;
  const aSignature: string; const aParams: array of const; aDomain: PMonoDomain = nil ): PMonoObject;
var
  m: PMonoMethod;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_object_new(aDomain, monoclass);
  m := FindMonoMethod(monoclass, '.ctor', aSignature);
  if m = nil then
    raise Exception.Create('constructor not found');
  CallMonoMethod(m, result, aParams);
end;

function TMonoJit.CreateMonoProxy(aObject: TObject;
  aMonoTypeName: string; aDomain: PMonoDomain): PMonoObject;
var
  mClass: PMonoClass;
  mIntfName: string;
begin
  mClass := FindMonoClass(FProxyNameSpace, FProxyClassName);
  mIntfName := GetClassIntf(aObject.ClassType, aMonoTypeName);
  result := CallMonoMethod(FindMonoMethod(mClass, 'CreateAsType', 'string,intptr'), nil, [mIntfName, pointer(aObject)], aDomain);
end;

{ TMonoConvert }

class function TMonoConvert.BooleanToMonoObj(Value: wordbool; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_boolean_class,
    Pointer(@Value));
end;

class function TMonoConvert.DateTimeToInt64(value: TDateTime;
  aDomain: PMonoDomain): Int64;
var
  c: PMonoClass;
  m: PMonoMethod;
  o,e: PMonoObject;
begin
  if not Assigned(FDateToBin) then
  begin
    c := MonoJit.FindMonoClass('System', 'DateTime');
    m := MonoJit.FindMonoMethod(c, 'ToBinary', '');
    FDateToBin := TDateTime_ToBinary(mono_method_get_unmanaged_thunk(m));
  end;
  o := DateTimeToMonoObj(value, aDomain);
  e := nil;
  result := FDateToBin(o, e);
  if e <> nil then
    raise Exception.Create(MonoJit.MonoExceptionMessage(e));
end;

class function TMonoConvert.DateTimeToMonoObj(Value: TDateTime;
  aDomain: PMonoDomain): PMonoObject;
var
  c: PMonoClass;
  m: PMonoMethod;
  e: PMonoObject;
begin
  if not Assigned(FFromOADate) then
  begin
    c := MonoJit.FindMonoClass('System', 'DateTime');
    m := MonoJit.FindMonoMethod(c, 'FromOADate', 'double');
    FFromOADate :=  TDateTime_FromOADate(mono_method_get_unmanaged_thunk(m));
  end;
  e := nil;
  result := FFromOADate(Value, e);
  if e <> nil then
    raise Exception.Create(MonoJit.MonoExceptionMessage(e));
end;

class function TMonoConvert.DoubleToMonoObj(Value: double; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_double_class,
    Pointer(@Value));
end;

class function TMonoConvert.DynArrayToMonoArray(aMonoType: PMonoType;
  const aValue: TValue; ArrayTypeInfo: PTypeInfo; aDomain: PMonoDomain = nil): PMonoArray;
var
  mt: PMonoType;
  dt: PTypeData;
  d_elem_size, d_arr_len: NativeUInt;
  i: integer;
  ms: PMonoString;
  p: Pointer;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  dt := GetTypeData(ArrayTypeInfo);

  d_elem_size := dt^.elSize;
  p := PPointer(aValue.GetReferenceToRawData)^;
  d_arr_len := aValue.GetArrayLength;


  if aMonoType <> nil then
  begin
    result := mono_array_new(aDomain, ElemClassArray(aMonoType), d_arr_len);
    mt := mono_class_get_type(ElemClassArray(aMonoType));
  end
  else begin
    result := nil;
    mt := nil;
  end;

  case dt^.elType2^.Kind of
    tkInteger:
      begin
        case d_elem_size of
          1: begin
            if mono_type_get_type(mt) in [MONO_TYPE_I1, MONO_TYPE_U1] then
              SetArrayValue(result, 1*d_arr_len, 0, PByte(p)[0])
            else
              raise Exception.Create('type error');
          end;
          2: begin
            if mono_type_get_type(mt) in [MONO_TYPE_I2, MONO_TYPE_U2] then
              SetArrayValue(result, 2*d_arr_len, 0, PByte(p)[0])
            else
              raise Exception.Create('type error');
          end;
          4: begin
            if mono_type_get_type(mt) in [MONO_TYPE_I4, MONO_TYPE_U4] then
              SetArrayValue(result, 4*d_arr_len, 0, PByte(p)[0])
            else
              raise Exception.Create('type error');
          end;
          8: begin
            if mono_type_get_type(mt) in [MONO_TYPE_I4, MONO_TYPE_U8] then
              SetArrayValue(result, 8*d_arr_len, 0, PByte(p)[0])
            else
              raise Exception.Create('type error');
          end;
        end;
      end;
    tkEnumeration:
      begin
        if mono_type_get_type(mt) in [MONO_TYPE_BOOLEAN] then
          SetArrayValue(result, 1*d_arr_len, 0, PByte(p)[0])
        else
          raise Exception.Create('type error');
      end;
    tkFloat:
      begin
        case d_elem_size of
          4: begin
            if mono_type_get_type(mt) in [MONO_TYPE_R4] then
              SetArrayValue(result, 4*d_arr_len, 0, PByte(p)[0])
            else
              raise Exception.Create('type error');
          end;
          8: begin
            if mono_type_get_type(mt) in [MONO_TYPE_R8] then
              SetArrayValue(result, 8*d_arr_len, 0, PByte(p)[0])
            else
              raise Exception.Create('type error');
          end;
        end;
      end;
    tkUString:
      begin
        if mono_type_get_type(mt) in [MONO_TYPE_STRING] then
        begin
          for i := 0 to d_arr_len - 1 do
          begin
            ms := TMonoConvert.StringToMonoStr(aValue.GetArrayElement(i).AsString, aDomain);
            SetArrayValue(result, 4, i, ms);
          end;
        end
        else
          raise Exception.Create('type error');
      end;
    tkWChar:
      begin
        if mono_type_get_type(mt) in [MONO_TYPE_CHAR] then
          SetArrayValue(result, d_elem_size*d_arr_len, 0, PByte(p)[0])
        else
          raise Exception.Create('type error');
      end;
  end;
end;

class function TMonoConvert.getDateTimeType: PMonoType;
var
  c: PMonoClass;
begin
  if not Assigned(FDateTimeType) then
  begin
    c := MonoJit.FindMonoClass('System', 'DateTime');
    FDateTimeType := mono_class_get_type(c);
  end;
  result := FDateTimeType;
end;

class function TMonoConvert.getDateTimeTypeRef: PMonoType;
var
  c: PMonoClass;
begin
  if not Assigned(FDateTimeTypeRef) then
  begin
    c := MonoJit.FindMonoClass('System', 'DateTime');
    FDateTimeTypeRef := mono_class_get_byref_type(c);
  end;
  result := FDateTimeTypeRef;
end;

class function TMonoConvert.Int16ToMonoObj(Value: smallint; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_int16_class, Pointer(@Value));
end;

class function TMonoConvert.Int64ToDateTime(value: Int64): TDateTime;
var
  c: PMonoClass;
  m: PMonoMethod;
  proc: TDateTime_FromBinary;
  o, e: PMonoObject;
begin
  if not Assigned(FDateFromBin) then
  begin
    c := MonoJit.FindMonoClass('System', 'DateTime');
    m := MonoJit.FindMonoMethod(c, 'FromBinary', 'long');
    FDateFromBin := TDateTime_FromBinary(mono_method_get_unmanaged_thunk(m));
  end;
  e := nil;
  o := FDateFromBin(Value, e);
  if e <> nil then
    raise Exception.Create(MonoJit.MonoExceptionMessage(e));
  result := MonoObjToDateTime(o);
end;

class function TMonoConvert.Int64ToMonoObj(Value: int64; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_int64_class, Pointer(@Value));
end;

class function TMonoConvert.Int8ToMonoObj(Value: shortint; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_sbyte_class, Pointer(@Value));
end;

class function TMonoConvert.IntToMonoObj(Value: integer; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_int32_class, Pointer(@Value));
end;

class procedure TMonoConvert.MonoArrayToDynArray(aArray: PMonoArray;
  aMonoType: PMonoType; var aResult: TValue; ArrayTypeInfo: PTypeInfo);
var
  mt: PMonoType;
  dt: PTypeData;
  d_elem_size, m_elem_size, m_arr_len: NativeUInt;
  i: integer;
  ms: PMonoString;
  p: Pointer;
begin
  if aMonoType <> nil then
    mt := mono_class_get_type(ElemClassArray(aMonoType))
  else
    mt := mono_class_get_type(mono_class_get_type(mono_object_get_class(PMonoObject(aArray)))^.data.klass);
  dt := GetTypeData(ArrayTypeInfo);
  d_elem_size := dt^.elSize;
  m_elem_size :=  mono_array_element_size(mono_object_get_class(PMonoObject(aArray)));

  m_arr_len := mono_array_length(aArray);

   case mono_type_get_type(mt) of
     MONO_TYPE_BOOLEAN: begin
       if (dt^.elType2^.Kind = tkEnumeration) then // 1 byte
       begin
         DynArraySetLength(p, ArrayTypeInfo, 1, @m_arr_len);
         GetArrayValue(aArray, m_arr_len*m_elem_size, 0, p^);
         TValue.Make(@p, ArrayTypeInfo, aResult);
         DynArrayClear(p, ArrayTypeInfo);
       end
       else
         raise Exception.Create('array type not compatible');
     end;
     MONO_TYPE_I1,MONO_TYPE_U1,MONO_TYPE_I2,MONO_TYPE_U2,
     MONO_TYPE_I4,MONO_TYPE_U4,MONO_TYPE_I8,MONO_TYPE_U8: begin
       if (dt^.elType2^.Kind = tkInteger)
           and (d_elem_size = m_elem_size) then
       begin
         DynArraySetLength(p, ArrayTypeInfo, 1, @m_arr_len);
         GetArrayValue(aArray, m_arr_len*m_elem_size, 0, p^);
         TValue.Make(@p, ArrayTypeInfo, aResult);
         DynArrayClear(p, ArrayTypeInfo);
       end
       else
         raise Exception.Create('array type not compatible');
     end;
     MONO_TYPE_R4, MONO_TYPE_R8: begin
       if (dt^.elType2^.Kind = tkFloat)  and (d_elem_size = m_elem_size) then
       begin
         DynArraySetLength(p, ArrayTypeInfo, 1, @m_arr_len);
         GetArrayValue(aArray, m_arr_len*m_elem_size, 0, p^);
         TValue.Make(@p, ArrayTypeInfo, aResult);
         DynArrayClear(p, ArrayTypeInfo);
       end
       else
         raise Exception.Create('array type not compatible');
     end;
     MONO_TYPE_STRING: begin
         if dt^.elType2^.Kind in [tkUString] then
         begin
           DynArraySetLength(p, ArrayTypeInfo, 1, @m_arr_len);
           TValue.Make(@p, ArrayTypeInfo, aResult);
           DynArrayClear(p, ArrayTypeInfo);
           for i := 0 to m_arr_len - 1 do
           begin
             GetArrayValue(aArray, 4, i, ms);
             aResult.SetArrayElement(i, TMonoConvert.MonoStrToString(ms));
           end;
         end
         else
           raise Exception.Create('array type not compatible');
     end;
     MONO_TYPE_CHAR: begin
         if (dt^.elType2^.Kind in [tkWChar])and (d_elem_size = m_elem_size) then
         begin
           DynArraySetLength(p, ArrayTypeInfo, 1, @m_arr_len);
           GetArrayValue(aArray, m_arr_len*m_elem_size, 0, p^);
           TValue.Make(@p, ArrayTypeInfo, aResult);
           DynArrayClear(p, ArrayTypeInfo);
         end
         else
           raise Exception.Create('array type not compatible');
       end;
   end;


end;

class procedure TMonoConvert.MonoArrayToVariant(aArray: PMonoArray; aMonoType: PMonoType;
  var aResult: TVarData);
var
  t: PMonoType;
  arr_len: NativeUInt;
  elem_size: integer;
  b2: ByteBool;
  i: integer;
  v: variant;
  ms: PMonoString;
  mo: PMonoObject;
  P: Pointer;
begin
  if aMonoType <> nil then
    t := mono_class_get_type(ElemClassArray(aMonoType))
  else
    t := mono_class_get_type(mono_class_get_type(mono_object_get_class(PMonoObject(aArray)))^.data.klass);
  elem_size :=  mono_array_element_size(mono_object_get_class(PMonoObject(aArray)));
  arr_len := mono_array_length(aArray);
  case mono_type_get_type(t) of
    MONO_TYPE_BOOLEAN: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varBoolean);
        for i := 0 to arr_len - 1 do
        begin
          GetArrayValue(aArray, elem_size, i, b2);
          variant(aResult)[i] := b2;
        end;
      end;
    MONO_TYPE_I1: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varShortInt);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_U1: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varByte);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_I2: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varSmallint);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 2, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_U2: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varWord);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 2, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_I4: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varInteger);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 4, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_U4: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varLongWord);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 4, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_I8: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varInt64);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 8, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_U8: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varUInt64);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 8, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_R4: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varSingle);
        P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 4, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_R8: begin
       variant(aResult) := VarArrayCreate([0,arr_len-1], varDouble);
       P := VarArrayLock(variant(aResult));
        try
          GetArrayValue(aArray, arr_len * 8, 0, P^);
        finally
          VarArrayUnlock(variant(aResult));
        end;
      end;
    MONO_TYPE_STRING: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varOleStr);
        for i := 0 to arr_len - 1 do
        begin
          GetArrayValue(aArray, 4, i, ms);
          variant(aResult)[i] := TMonoConvert.MonoStrToString(ms);
        end;
      end;
    MONO_TYPE_CLASS: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], VarMono);
        for i := 0 to arr_len - 1 do
        begin
          GetArrayValue(aArray, 4, i, mo);
          variant(aResult)[i] := MonoObj2V(mo);
        end;
      end;
    MONO_TYPE_SZARRAY: begin
        variant(aResult) := VarArrayCreate([0,arr_len-1], varVariant);
        for i := 0 to arr_len - 1 do
        begin
          GetArrayValue(aArray, 4, i, mo);
          MonoArrayToVariant(PMonoArray(mo), nil, TVarData(v));
          variant(aResult)[i] := v;
        end;
      end;
  end;
end;

class function TMonoConvert.MonoObjToBoolean(Value: PMonoObject): wordbool;
begin
  result := PWordBool(mono_object_unbox(Value))^;
end;


class function TMonoConvert.MonoObjToDateTime(Value: PMonoObject): TDateTime;
var
  m: PMonoMethod;
  e: PMonoObject;
begin
  if not Assigned(FToOADate) then
  begin
    m := MonoJit.FindMonoMethod(mono_object_get_class(Value), 'ToOADate', '');
    FToOADate :=  TDateTime_ToOADate(mono_method_get_unmanaged_thunk(m));
  end;
  e := nil;
  result := FToOADate(Value, e);
  if e <> nil then
    raise Exception.Create(MonoJit.MonoExceptionMessage(e));
end;

class function TMonoConvert.MonoObjToDouble(Value: PMonoObject): double;
begin
  result := PDouble(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToInt(Value: PMonoObject): integer;
begin
  result := PInteger(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToInt16(Value: PMonoObject): smallint;
begin
  result := PSmallInt(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToInt64(Value: PMonoObject): int64;
begin
  result := PInt64(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToInt8(Value: PMonoObject): shortint;
begin
  result := PShortInt(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToPointer(Value: PMonoObject): Pointer;
begin
  result := PPointer(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToSingle(Value: PMonoObject): single;
begin
  result := PSingle(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToSmallInt(Value: PMonoObject): smallint;
begin
  result := PSmallInt(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToUInt(Value: PMonoObject): cardinal;
begin
  result := PCardinal(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToUInt16(Value: PMonoObject): word;
begin
  result := PWord(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToUInt64(Value: PMonoObject): UInt64;
begin
  result := PUInt64(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoObjToUInt8(Value: PMonoObject): byte;
begin
  result := PByte(mono_object_unbox(Value))^;
end;

class function TMonoConvert.MonoStrToString(Value: PMonoString): String;
begin
  // setlength(result, value.length);
  result := mono_string_to_utf16(Value);
  // mono_string_to_byvalwstr(Pointer(result), value, value.length);
end;

class function TMonoConvert.PointerToMonoObj(Value: Pointer; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_intptr_class, Pointer(@Value));
end;

class function TMonoConvert.SingleToMonoObj(Value: single; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_single_class,
    Pointer(@Value));
end;

class function TMonoConvert.SmallIntToMonoObj(Value: smallint; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_int16_class, Pointer(@Value));
end;

class function TMonoConvert.UInt16ToMonoObj(Value: word; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_uint16_class, Pointer(@Value));
end;

class function TMonoConvert.UInt64ToMonoObj(Value: UInt64; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_uint64_class, Pointer(@Value));
end;

class function TMonoConvert.UInt8ToMonoObj(Value: byte; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_byte_class, Pointer(@Value));
end;

class function TMonoConvert.UIntToMonoObj(Value: cardinal; aDomain: PMonoDomain = nil): PMonoObject;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  result := mono_value_box(aDomain, mono_get_uint32_class, Pointer(@Value));
end;

class function TMonoConvert.VariantToMonoArray(aMonoType: PMonoType;
  const aValue: TVarData; aDomain: PMonoDomain = nil): PMonoArray;
var
  p: Pointer;
  lsize, i: integer;
  b: byteBool;
  s: string;
  ms: PMonoString;
  mo: PMonoObject;
  v: variant;
  elemType: TMonoTypeEnum;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();
  lsize := VarArrayHighBound(variant(aValue), 1)+1;

  if aMonoType <> nil then
  begin
    result := mono_array_new(aDomain, ElemClassArray(aMonoType), lsize);
    elemType := mono_type_get_type(mono_class_get_type(ElemClassArray(aMonoType)));
  end
  else begin
    result := nil;
    elemType := MONO_TYPE_END;
  end;


  case (aValue.VType and varTypeMask) of
    varByte:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_byte_class, lsize)
        else
          if elemType <> MONO_TYPE_U1 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varShortInt:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_sbyte_class, lsize)
        else
          if elemType <> MONO_TYPE_I1 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varWord:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_uint16_class, lsize)
        else
          if elemType <> MONO_TYPE_U2 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 2*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varSmallint:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_int16_class, lsize)
        else
          if elemType <> MONO_TYPE_I2 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 2*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varLongWord:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_uint32_class, lsize)
        else
          if elemType <> MONO_TYPE_U4 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 4*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varInteger:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_int32_class, lsize)
        else
          if elemType <> MONO_TYPE_I4 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 4*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varUInt64:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_uint64_class, lsize)
        else
          if elemType <> MONO_TYPE_U8 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 8*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varInt64:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_int64_class, lsize)
        else
          if elemType <> MONO_TYPE_I8 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 8*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varSingle:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_single_class, lsize)
        else
          if elemType <> MONO_TYPE_R4 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 4*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varDouble:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_double_class, lsize)
        else
          if elemType <> MONO_TYPE_R8 then
            raise Exception.Create('Array Type error');

        P := VarArrayLock(variant(aValue));
        try
          SetArrayValue(result, 8*lSize, 0, P^);
        finally
          VarArrayUnlock(variant(aValue));
        end;
      end;
    varBoolean:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_boolean_class, lsize)
        else
          if elemType <> MONO_TYPE_BOOLEAN then
            raise Exception.Create('Array Type error');

        for i := 0 to lsize-1 do
        begin
          b := variant(aValue)[i];
          SetArrayValue(result, 1, i, b);
        end;
      end;
    varOleStr:
      begin
        if result = nil then
          result := mono_array_new(aDomain, mono_get_string_class, lsize)
        else
          if elemType <> MONO_TYPE_STRING then
            raise Exception.Create('Array Type error');

        for i := 0 to lsize-1 do
        begin
          s := variant(aValue)[i];
          ms := TMonoConvert.StringToMonoStr(s, aDomain);
          SetArrayValue(result, 4, i, ms);
        end;
      end;
    varVariant:
      begin
        //decodifica a mono obj
        if result = nil then
          result := mono_array_new(aDomain, mono_get_object_class, lsize)
        else
          if elemType <> MONO_TYPE_OBJECT then
            raise Exception.Create('Array Type error');

        for i := 0 to lsize-1 do
        begin
          mo := nil;
          v := variant(aValue)[i];
          if VarIsArray(v) then
          begin
            mo := PMonoObject(VariantToMonoArray(nil, TVarData(v)));
          end
          else case VarType(v) of
            varByte: mo := TMonoConvert.UInt8ToMonoObj(v, aDomain);
            varShortInt: mo := TMonoConvert.Int8ToMonoObj(v, aDomain);
            varWord: mo := TMonoConvert.UInt16ToMonoObj(v, aDomain);
            varSmallint: mo := TMonoConvert.Int64ToMonoObj(v, aDomain);
            varInteger: mo := TMonoConvert.IntToMonoObj(v, aDomain);
            varLongWord: mo := TMonoConvert.UIntToMonoObj(v, aDomain);
            varInt64: mo := TMonoConvert.Int64ToMonoObj(v, aDomain);
            varUInt64: mo := TMonoConvert.UInt64ToMonoObj(v, aDomain);
            varSingle: mo := TMonoConvert.SingleToMonoObj(v, aDomain);
            varDouble: mo := TMonoConvert.DoubleToMonoObj(v, aDomain);
            varCurrency: mo := TMonoConvert.DoubleToMonoObj(v, aDomain);
            varBoolean: mo := TMonoConvert.BooleanToMonoObj(v, aDomain);
            varString,varOleStr,varUString: mo := PMonoObject(TMonoConvert.StringToMonoStr(v, aDomain));
          end;
          SetArrayValue(result, 4, i, mo);
        end;
      end;
  end;

  if result = nil then
    if (aValue.VType and varTypeMask) = VarMono then
    begin
      result := mono_array_new(aDomain, mono_get_object_class, lsize);
      for i := 0 to lsize-1 do
      begin
        mo := V2MonoObject(variant(aValue)[i]);
        SetArrayValue(result, 4, i, mo);
      end;
    end;

end;

class function TMonoConvert.StringToMonoStr(Value: String; aDomain: PMonoDomain = nil): PMonoString;
begin
  if aDomain = nil then
    aDomain := mono_domain_get();

  result := mono_string_new_utf16(aDomain, PChar(Value), Length(Value));
end;

{ TMonoInvoke }

procedure TMonoInvoke.AddBoolArg(aValue: wordbool; aMonoType: PMonoType);
var
  p: PWordBool;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atBool));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddCharArg(aValue: char; aMonoType: PMonoType);
var
  p: PChar;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atChar));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddDateTimeArg(aValue: TDateTime; aMonoType: PMonoType);
var
  p: PInt64;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atDateTime));
  new(p);
  p^ := TMonoConvert.DateTimeToInt64(aValue, FDomain);
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddDoubleArg(aValue: double; aMonoType: PMonoType);
var
  p: PDouble;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atDouble));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddInt16Arg(aValue: SmallInt; aMonoType: PMonoType);
var
  p: PSmallInt;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atInt16));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddInt64Arg(aValue: int64; aMonoType: PMonoType);
var
  p: PInt64;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atInt64));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddInt8Arg(aValue: ShortInt; aMonoType: PMonoType);
var
  p: PShortInt;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atInt8));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddIntArg(aValue: integer; aMonoType: PMonoType);
var
  p: PInteger;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atInt32));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddObjectArg(aValue: TObject; aMonoType: PMonoType);
var
  p: PMonoObject;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, false, atObject));
  p := MonoJit.CreateMonoProxy(aValue, '', FDomain);
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddSingleArg(aValue: single; aMonoType: PMonoType);
var
  p: PSingle;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atSingle));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddStrArg(aValue: widestring; aMonoType: PMonoType);
var
  p: PMonoString;
  p2: PPMonoString;
begin
  if mono_type_is_byref(aMonoType) then
  begin
    new(p2);
    FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atString));
    p2^ := TMonoConvert.StringToMonoStr(aValue, FDomain);
    FArgs.Add(p2);
  end
  else begin
    FArgTypes.Add(TRecArgInfo.Create(aMonoType, false, atString));
    p := TMonoConvert.StringToMonoStr(aValue, FDomain);
    FArgs.Add(p);
  end;
end;

procedure TMonoInvoke.AddUInt16Arg(aValue: Word; aMonoType: PMonoType);
var
  p: PWord;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atUInt16));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddUInt64Arg(aValue: UInt64; aMonoType: PMonoType);
var
  p: PUInt64;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atUInt64));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddUInt8Arg(aValue: Byte; aMonoType: PMonoType);
var
  p: PByte;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atUInt8));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddUIntArg(aValue: Cardinal; aMonoType: PMonoType);
var
  p: PCardinal;
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, true, atUInt32));
  new(p);
  p^ := aValue;
  FArgs.Add(p);
end;

procedure TMonoInvoke.AddVarData(aMonoType: PMonoType; aValue: TVarData);
var
  isByRef: longbool;
  p: PVarData;
  a: PMonoArray;
  pp: PPointer;
  s: string;
  o: PMonoObject;
  b: wordBool;
  t: PMonoType;
begin
  isByRef := mono_type_is_byref(aMonoType);
  case mono_type_get_type(aMonoType) of
    MONO_TYPE_I1:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddInt8Arg(p^.VShortInt, aMonoType);
              end;
            varShortInt:
               AddInt8Arg(PshortInt(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddInt8Arg(aValue.VShortInt, aMonoType);
        end;
      end;
    MONO_TYPE_U1:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddUInt8Arg(p^.VByte, aMonoType);
              end;
            varShortInt:
               AddUInt8Arg(PByte(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddUInt8Arg(aValue.VByte, aMonoType);
        end;
      end;
    MONO_TYPE_I2:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddInt16Arg(p^.VSmallInt, aMonoType);
              end;
            varShortInt:
               AddInt16Arg(PSmallInt(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddInt16Arg(aValue.VSmallInt, aMonoType);
        end;
      end;
    MONO_TYPE_U2:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddUInt16Arg(p^.VWord, aMonoType);
              end;
            varShortInt:
               AddUInt16Arg(PWord(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddUInt16Arg(aValue.VWord, aMonoType);
        end;
      end;
    MONO_TYPE_I4:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddIntArg(p^.VInteger, aMonoType);
              end;
            varInteger:
               AddIntArg(PInteger(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddIntArg(aValue.VInteger, aMonoType);
        end;
      end;
    MONO_TYPE_U4:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddUIntArg(p^.VLongWord, aMonoType);
              end;
            varLongWord:
               AddUIntArg(PCardinal(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddUIntArg(aValue.VLongWord, aMonoType);
        end;
      end;
    MONO_TYPE_I8:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddInt64Arg(p^.VInt64, aMonoType);
              end;
            varInt64:
               AddInt64Arg(PInt64(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddInt64Arg(aValue.VInt64, aMonoType);
        end;
      end;
    MONO_TYPE_U8:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddUInt64Arg(p^.VUInt64, aMonoType);
              end;
            varUInt64:
               AddInt64Arg(PUint64(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          AddUInt64Arg(aValue.VUInt64, aMonoType);
        end;
      end;
    MONO_TYPE_R4:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddSingleArg(Variant(p^), aMonoType);
              end;
            varSingle:
               AddSingleArg(PSingle(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else
          AddSingleArg(variant(aValue), aMonoType);
      end;
    MONO_TYPE_R8:
       begin
          if VarIsByRef(variant(aValue)) then
          begin
            case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddDoubleArg(Variant(p^), aMonoType);
              end;
            varDouble:
               AddDoubleArg(PDouble(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
            end;
          end
          else
            AddDoubleArg(variant(aValue), aMonoType);
       end;
    MONO_TYPE_BOOLEAN:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                p := AValue.VPointer;
                AddBoolArg(Variant(p^), aMonoType);
              end;
            varBoolean:
               AddBoolArg(PWordBool(AValue.VPointer)^, aMonoType);
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else
          AddBoolArg(variant(AValue), aMonoType);
      end;
    MONO_TYPE_STRING:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                s := variant(PVarData(aValue.VPointer)^);
                AddStrArg(s, aMonoType);
              end;
            varOleStr: begin
                s := variant(aValue);
                AddStrArg(s, aMonoType);
              end
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
        end
        else begin
          s := Variant(aValue);
          AddStrArg(s, aMonoType);
        end;
      end;
    MONO_TYPE_CLASS:
      begin
        if VarIsByRef(variant(aValue)) then
        begin
          case (AValue.VType and varTypeMask) of
            varInteger:
                AddObjectArg(TObject(PInteger(AValue.VPointer)^), aMonoType);
          end;
        end
        else
          AddObjectArg(V2O(variant(aValue)), aMonoType);
      end;
    MONO_TYPE_SZARRAY:
      begin
        if VarIsArray(variant(avalue)) then
          if VarIsByRef(variant(avalue)) then
          begin
            p := AValue.VPointer;
            if isByRef then
            begin
              new(pp);
              PPMonoArray(pp)^ := TMonoConvert.VariantToMonoArray(aMonoType, p^);
              AddPtrArg(pp, aMonoType, true, atArray);
            end
            else begin
              a := TMonoConvert.VariantToMonoArray(aMonoType, p^);
              AddPtrArg(a, aMonoType, false, atArray);
            end;
          end
          else begin
            // array di variant -> cast a monoobject
            a := TMonoConvert.VariantToMonoArray(aMonoType, aValue);
            AddPtrArg(a, aMonoType, false, atArray);
          end;
      end;
    MONO_TYPE_GENERICINST: begin
      if mono_class_is_valuetype(mono_class_from_mono_type(aMonoType)) then
      begin
        if StartsText('System.Nullable', UTF8ToString( mono_type_get_name(aMonoType))) then //'System.Nullable<System.Int32>'
        begin
          if VarIsNull(variant(aValue)) then
          begin

              AddPtrArg(nil, aMonoType, false, atPointer);
          end
          else begin
            //nullable object : ValueBox
             t := mono_field_get_type(mono_class_get_field_from_name(mono_class_from_mono_type(aMonoType), 'value'));
             o := nil;
             case mono_type_get_type(t) of
               MONO_TYPE_I1: o := TMonoConvert.Int8ToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_I2: o := TMonoConvert.Int16ToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_I4: o := TMonoConvert.IntToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_I8: o := TMonoConvert.Int64ToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_U1: o := TMonoConvert.UInt8ToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_U2: o := TMonoConvert.UInt16ToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_U4: o := TMonoConvert.UIntToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_U8: o := TMonoConvert.UInt64ToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_R4: o := TMonoConvert.SingleToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_R8: o := TMonoConvert.DoubleToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_BOOLEAN: o := TMonoConvert.BooleanToMonoObj(variant(aValue), FDomain);
               MONO_TYPE_VALUETYPE: begin
                 if (aMonoType = TMonoConvert.DateTimeType) then
                   o := TMonoConvert.DateTimeToMonoObj(variant(aValue), FDomain);
               end;
             end;
               AddPtrArg(o, aMonoType, false, atPointer);
          end;
        end;
      end;
    end;
    MONO_TYPE_VALUETYPE:
      begin
        if (aMonoType = TMonoConvert.DateTimeType)or(aMonoType = TMonoConvert.DateTimeTypeRef) then
        //if MatchStr(UTF8ToString(mono_type_get_name(aMonoType)), ['System.DateTime&','System.DateTime']) then
          if VarIsByRef(variant(aValue)) then
          begin
            case (AValue.VType and varTypeMask) of
              varVariant: begin
                  p := AValue.VPointer;
                  AddDateTimeArg(Variant(p^), aMonoType);
                end;
              varDate:
                 AddDateTimeArg(pDateTime(AValue.VPointer)^, aMonoType);
              else
                raise Exception.Create('Variant ByRef not supported');
            end;
          end
          else
            AddDateTimeArg(variant(AValue), aMonoType);
      end;
  end;

end;

procedure TMonoInvoke.AddVarRec(aMonoType: PMonoType; aValue: TVarRec);
var
  isByRef: longbool;
  s: string;
begin
  isByRef := mono_type_is_byref(aMonoType);
  case mono_type_get_type(aMonoType) of
    MONO_TYPE_I: // Pointer - nativeInt
      begin
        if aValue.VType = vtPointer then
        begin
          if sizeof(Pointer) = 4 then
            AddIntArg(aValue.VInteger, aMonoType)
          else
            AddInt64Arg(aValue.VInt64^, aMonoType);
        end;
      end;
    MONO_TYPE_I1:
      begin
        if aValue.VType = vtPointer then
        begin
          AddInt8Arg(PInteger(aValue.VPointer)^, aMonoType);
        end
        else
          AddInt8Arg(aValue.VInteger, aMonoType);
      end;
    MONO_TYPE_U1:
      begin
        if aValue.VType = vtPointer then
        begin
          AddUInt8Arg(PInteger(aValue.VPointer)^, aMonoType);
        end
        else
          AddUInt8Arg(aValue.VInteger, aMonoType);
      end;
    MONO_TYPE_I2:
      begin
        if aValue.VType = vtPointer then
        begin
          AddInt16Arg(PInteger(aValue.VPointer)^, aMonoType);
        end
        else
          AddInt16Arg(aValue.VInteger, aMonoType);
      end;
    MONO_TYPE_U2:
      begin
        if aValue.VType = vtPointer then
        begin
          AddUInt16Arg(PInteger(aValue.VPointer)^, aMonoType);
        end
        else
          AddUInt16Arg(aValue.VInteger, aMonoType);
      end;
    MONO_TYPE_I4:
      begin
        if aValue.VType = vtPointer then
        begin
          if isByRef then
            AddPtrArg(aValue.VPointer, aMonoType, false, atPointer)
          else
            AddIntArg(PInteger(aValue.VPointer)^, aMonoType);
        end
        else
          AddIntArg(aValue.VInteger, aMonoType);
      end;
    MONO_TYPE_U4:
      begin
        if aValue.VType = vtPointer then
        begin
          if isByRef then
            AddPtrArg(aValue.VPointer, aMonoType, false, atPointer)
          else
            AddUIntArg(PCardinal(aValue.VPointer)^, aMonoType);
        end
        else
          AddUIntArg(aValue.VInteger, aMonoType);
      end;
    MONO_TYPE_U8:
      begin
        if aValue.VType = vtPointer then
        begin
          if isByRef then
            AddPtrArg(aValue.VPointer, aMonoType, false, atPointer)
          else
            AddUInt16Arg(PUInt64(aValue.VPointer)^, aMonoType);
        end
        else
          AddUInt64Arg(aValue.VInt64^, aMonoType);
      end;
    MONO_TYPE_I8:
      begin
        if aValue.VType = vtPointer then
        begin
          if isByRef then
            AddPtrArg(aValue.VPointer, aMonoType, false, atPointer)
          else
            AddInt64Arg(PInt64(aValue.VPointer)^, aMonoType);
        end
        else
          AddInt64Arg(aValue.VInt64^, aMonoType);
      end;
    MONO_TYPE_R4:
      begin
        if aValue.VType = vtPointer then
        begin
          AddSingleArg(PExtended(aValue.VPointer)^, aMonoType);
        end
        else
          AddSingleArg(aValue.VExtended^, aMonoType);
      end;
    MONO_TYPE_R8:
      begin
        if aValue.VType = vtPointer then
        begin
          AddPtrArg(aValue.VPointer, aMonoType, false, atPointer);
        end
        else
          AddDoubleArg(aValue.VExtended^, aMonoType);
      end;
    MONO_TYPE_BOOLEAN:
      begin
        if aValue.VType = vtPointer then
        begin
          AddBoolArg(Boolean(aValue.VPointer^), aMonoType);
        end
        else
          AddBoolArg(aValue.VBoolean, aMonoType);
      end;
    MONO_TYPE_CHAR:
      begin
        if aValue.VType = vtPointer then
        begin
          AddCharArg(Char(aValue.VPointer^), aMonoType);
        end
        else
          AddCharArg(aValue.VWideChar, aMonoType);
      end;
    MONO_TYPE_STRING:
      begin
        if aValue.VType = vtPointer then
        begin
          s := string(aValue.VPointer^);
        end
        else
          s := PChar(aValue.VUnicodeString);
        AddStrArg(s, aMonoType);
      end;
    MONO_TYPE_CLASS:
      begin
        if aValue.VType = vtPointer then
        begin
          AddObjectArg(TObject(aValue.VPointer^), aMonoType);
        end
        else
          AddObjectArg(aValue.VObject, aMonoType);
      end;
    MONO_TYPE_GENERICINST:
      begin
        AddPtrArg(aValue.VPointer, aMonoType, false, atObject)
      end;
    MONO_TYPE_VALUETYPE:
      begin
        if (aMonoType = TMonoConvert.DateTimeType)Or(aMonoType = TMonoConvert.DateTimeTypeRef) then
        begin
          if aValue.VType = vtPointer then
          begin
            AddDateTimeArg(PExtended(aValue.VPointer)^, aMonoType);
          end
          else
            AddDateTimeArg(aValue.VExtended^, aMonoType);
        end
        else
          raise Exception.Create('param type error ' +
       MonoTypeEnumToString(mono_type_get_type(aMonoType)));
      end
  else
    raise Exception.Create('param type error ' +
       MonoTypeEnumToString(mono_type_get_type(aMonoType)));
  end;
end;

procedure TMonoInvoke.AddPtrArg(aValue: Pointer; aMonoType: PMonoType; aDispose: boolean; aArgTyp: TRecArgTyp);
begin
  FArgTypes.Add(TRecArgInfo.Create(aMonoType, aDispose, aArgTyp));
  FArgs.Add(aValue);
end;

procedure TMonoInvoke.AfterConstruction;
begin
  inherited;
  FArgs := TList.Create;
  FArgTypes := TList<TRecArgInfo>.Create;
end;

procedure TMonoInvoke.BeforeDestruction;
begin
  inherited;
  ClearArgs;
  FArgs.Free;
  FArgTypes.Free;
end;

procedure TMonoInvoke.ClearArgs;
var
  i: integer;
begin
  for i := 0 to FArgTypes.Count - 1 do
  begin
    if FArgs[i] = nil then
        continue;

    if FArgTypes[i]._Dispose then
    begin
      case FArgTypes[i].ArgTyp of
        atUInt8: dispose(PByte(FArgs[i]));
        atInt8: dispose(PShortInt(FArgs[i]));
        atUInt16: dispose(PWord(FArgs[i]));
        atInt16: dispose(PSmallInt(FArgs[i]));
        atUInt32: dispose(PCardinal(FArgs[i]));
        atInt32: dispose(PInteger(FArgs[i]));
        atUInt64: dispose(PUint64(FArgs[i]));
        atInt64: dispose(PInt64(FArgs[i]));
        atSingle: dispose(PSingle(FArgs[i]));
        atDouble: dispose(PDouble(FArgs[i]));
        atBool: dispose(PWordBool(FArgs[i]));
        atChar: dispose(PChar(FArgs[i]));
        atString: begin
          PPMonoString(FArgs[i])^ := nil;
          dispose(PPMonoString(FArgs[i]));
        end;
        atArray: begin
          PPMonoArray(FArgs[i])^ := nil;
          dispose(PPMonoArray(FArgs[i]));
        end;
        atDateTime: begin
          dispose(PInt64(FArgs[i]));
        end;
      end;
    end;
  end;
  FArgTypes.Clear;
  FArgs.Clear;
  FResult := nil;
end;

constructor TMonoInvoke.Create(aDomain: PMonoDomain);
begin
  inherited Create;
  FDomain := aDomain;
end;



function TMonoInvoke.MonoInvoke(aObject: PMonoObject;
  aMethod: PMonoMethod): boolean;
var
  p: Pointer;
begin
  FResult := nil;
  FMonoException := nil;
  p := nil;
  if FArgs.Count = 0 then
    FResult := mono_runtime_invoke(aMethod, PPointer(aObject), p,
      FMonoException)
  else
    FResult := mono_runtime_invoke(aMethod, PPointer(aObject), FArgs.List^[0],
      FMonoException);
  result := FMonoException = nil;
end;

function TMonoInvoke.MonoInvokeByName(aClass: PMonoClass;
  aMethodName: string): boolean;
var
  m: PMonoMethod;
  lMethodName: UTF8String;
begin
  lMethodName := UTF8Encode(aMethodName);
  m := mono_class_get_method_from_name(aClass, PAnsiChar(lMethodName),
    FArgs.Count);
  if m <> nil then
    result := MonoInvoke(nil, m)
  else
    result := false;
end;

function TMonoInvoke.MonoInvokeVirtual(aClass: PMonoClass;
  aObject: PMonoObject; aMethodName: string): boolean;
var
  m: PMonoMethod;
  lMethodName: UTF8String;
begin
  lMethodName := UTF8Encode(aMethodName);
  m := mono_class_get_method_from_name(aClass, PAnsiChar(lMethodName),
    FArgs.Count);
  if m <> nil then
  begin
    m := mono_object_get_virtual_method(aObject, m);
    result := MonoInvoke(aObject, m);
  end
  else
    result := false;

end;

function TMonoInvoke.MonoInvokeByName(aObject: PMonoObject;
  aMethodName: string): boolean;
var
  lClass: PMonoClass;
  m: PMonoMethod;
  lMethodName: UTF8String;
begin
  lMethodName := UTF8Encode(aMethodName);
  lClass := mono_object_get_class(aObject);
  if lClass = nil then
    ShowMessage('class = nil');
  m := mono_class_get_method_from_name(lClass, PAnsiChar(lMethodName),
    FArgs.Count);
  if m <> nil then
    result := MonoInvoke(aObject, m)
  else
  begin
    if lClass <> nil then
      ShowMessage('Method not found ' + mono_class_get_name(lClass)
          + '.' + aMethodName);
    result := false;
  end;
end;

function TMonoInvoke.ResultAsBoolean: wordbool;
begin
  result := TMonoConvert.MonoObjToBoolean(FResult);
end;

function TMonoInvoke.ResultAsDouble: double;
begin
  result := TMonoConvert.MonoObjToDouble(FResult);
end;

function TMonoInvoke.ResultAsInteger: integer;
begin
  result := TMonoConvert.MonoObjToInt(FResult);
end;

function TMonoInvoke.ResultAsString: string;
begin
  result := TMonoConvert.MonoStrToString(PMonoString(FResult));
end;


procedure TMonoInvoke.SetResultVarData(aMonoType: PMonoType;
  aObjResult: PMonoObject; var aResult: TVarData);
var
  s: string;
  b: PWordBool;
begin
  case mono_type_get_type(aMonoType) of
    MONO_TYPE_VOID: variant(aResult) := Null;
    MONO_TYPE_BOOLEAN: variant(aResult) := TMonoConvert.MonoObjToBoolean(aObjResult);
    MONO_TYPE_I1: variant(aResult) := TMonoConvert.MonoObjToInt8(aObjResult);
    MONO_TYPE_U1: variant(aResult) := TMonoConvert.MonoObjToUInt8(aObjResult);
    MONO_TYPE_I2: variant(aResult) := TMonoConvert.MonoObjToInt16(aObjResult);
    MONO_TYPE_U2: variant(aResult) := TMonoConvert.MonoObjToUInt16(aObjResult);
    MONO_TYPE_I4: variant(aResult) := TMonoConvert.MonoObjToInt(aObjResult);
    MONO_TYPE_U4: variant(aResult) := TMonoConvert.MonoObjToUInt(aObjResult);
    MONO_TYPE_I8: variant(aResult) := TMonoConvert.MonoObjToInt64(aObjResult);
    MONO_TYPE_U8: variant(aResult) := TMonoConvert.MonoObjToUInt64(aObjResult);
    MONO_TYPE_R4: variant(aResult) := TMonoConvert.MonoObjToSingle(aObjResult);
    MONO_TYPE_R8: variant(aResult) := TMonoConvert.MonoObjToDouble(aObjResult);
    MONO_TYPE_STRING: begin
      if aObjResult = nil then
        variant(aResult) := NULL
      else begin
        s := TMonoConvert.MonoStrToString(PMonoString(aObjResult));
        variant(aResult) := s;
      end;
    end;
    MONO_TYPE_CLASS: MonoObj2V(aObjResult, aResult);
    MONO_TYPE_SZARRAY: TMonoConvert.MonoArrayToVariant(PMonoArray(aObjResult), aMonoType, aResult);
    MONO_TYPE_GENERICINST: begin
      if mono_class_is_valuetype(mono_class_from_mono_type(aMonoType)) then
      begin
        if StartsText('System.Nullable', UTF8ToString(mono_type_get_name(aMonoType))) then //'System.Nullable<System.Int32>'
        begin
          if aObjResult = nil then
            variant(aResult) := NULL
          else
            SetResultVarData(mono_field_get_type(mono_class_get_field_from_name(mono_class_from_mono_type(aMonoType), 'value')), aObjResult, aResult);
        end;
      end
      else
        MonoObj2V(aObjResult, aResult);
    end;
    MONO_TYPE_VALUETYPE: begin
        if mono_class_get_name(mono_object_get_class(aObjResult)) = 'DateTime' then
          variant(aResult) := TMonoConvert.MonoObjToDateTime(aObjResult)
        else
          MonoObj2V(aObjResult, aResult);
      end;
  else
    variant(aResult) := null;
  end;
end;

procedure TMonoInvoke.SetVarData(aIndex: integer; var aValue: TVarData);
var
  t: PMonoType;
begin
  if (VarIsByRef(variant(aValue)))and(mono_type_is_byref(FArgTypes[aIndex].MonoType)) then
  begin
    case mono_type_get_type(FArgTypes[aIndex].MonoType) of
      MONO_TYPE_BOOLEAN:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) :=  PWordBool(FArgs[aIndex])^;
          varBoolean:
               PWordBool(AValue.VPointer)^ := PWordBool(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_I1:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PShortInt(FArgs[aIndex])^;
          varShortInt:
             PShortInt(AValue.VPointer)^ := PShortInt(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_U1:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PByte(FArgs[aIndex])^;
          varByte:
             PByte(AValue.VPointer)^ := PByte(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_I2:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PSmallInt(FArgs[aIndex])^;
          varSmallint:
             PSmallInt(AValue.VPointer)^ := PSmallInt(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_U2:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PWord(FArgs[aIndex])^;
          varInteger:
             PWord(AValue.VPointer)^ := PWord(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_I4:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PInteger(FArgs[aIndex])^;
          varInteger:
             PInteger(AValue.VPointer)^ := PInteger(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_U4:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PCardinal(FArgs[aIndex])^;
          varLongWord:
             PCardinal(AValue.VPointer)^ := PCardinal(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_I8:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PInt64(FArgs[aIndex])^;
          varInt64:
             PInt64(AValue.VPointer)^ := PInt64(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_U8:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PUInt64(FArgs[aIndex])^;
          varUInt64:
             PUInt64(AValue.VPointer)^ := PUInt64(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_R4:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := Psingle(FArgs[aIndex])^;
          varSingle:
              PSingle(AValue.VPointer)^ := PSingle(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_R8:
      begin
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := PDouble(FArgs[aIndex])^;
          varDouble:
              PDouble(AValue.VPointer)^ := PDouble(FArgs[aIndex])^;
        end;
      end;
      MONO_TYPE_STRING:
      begin
          case (AValue.VType and varTypeMask) of
            varVariant: begin
                variant(PVarData(aValue.VPointer)^) := TMonoConvert.MonoStrToString(PPMonoString(FArgs[aIndex])^);
              end;
            varOleStr: begin
                variant(aValue) := TMonoConvert.MonoStrToString(PPMonoString(FArgs[aIndex])^);
              end
            else
              raise Exception.Create('Variant ByRef not supported');
          end;
      end;
      MONO_TYPE_SZARRAY: TMonoConvert.MonoArrayToVariant(PPMonoArray(FArgs[aIndex])^, FArgTypes[aIndex].MonoType, PVarData(AValue.VPointer)^);
      MONO_TYPE_GENERICINST: begin
        if mono_class_is_valuetype(mono_class_from_mono_type(FArgTypes[aIndex].MonoType)) then
        begin
          if StartsText('System.Nullable', UTF8ToString(mono_type_get_name(FArgTypes[aIndex].MonoType))) then //'System.Nullable<System.Int32>'
          begin
            if (AValue.VType and varTypeMask) = varVariant then
            begin
              if PMonoObject(FArgs[aIndex]) = nil then
                variant(PVarData(AValue.VPointer)^) := NULL
              else begin
                 t := mono_field_get_type(mono_class_get_field_from_name(mono_class_from_mono_type(FArgTypes[aIndex].MonoType), 'value'));
                 case mono_type_get_type(t) of
                   MONO_TYPE_I1: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToInt8(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_I2: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToInt16(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_I4: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToInt(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_I8: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToInt64(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_U1: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToUInt8(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_U2: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToUInt16(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_U4: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToUInt(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_U8: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToUInt64(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_R4: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToSingle(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_R8: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToDouble(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_BOOLEAN: variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToBoolean(PMonoObject(FArgs[aIndex]));
                   MONO_TYPE_VALUETYPE: begin
                     if (FArgTypes[aIndex].MonoType = TMonoConvert.DateTimeType) then
                       variant(PVarData(AValue.VPointer)^) := TMonoConvert.MonoObjToDateTime(PMonoObject(FArgs[aIndex]));
                   end;
                  end;
                end;
            end;
          end;
        end;
      end;
      MONO_TYPE_VALUETYPE:
      begin
        if (FArgTypes[aIndex].MonoType = TMonoConvert.DateTimeType)or(FArgTypes[aIndex].MonoType = TMonoConvert.DateTimeTypeRef) then
        //if MatchStr(UTF8ToString(mono_type_get_name(FArgTypes[aIndex].MonoType)), ['System.DateTime&','System.DateTime']) then
        case (AValue.VType and varTypeMask) of
          varVariant:
              variant(PVarData(AValue.VPointer)^) := TMonoConvert.Int64ToDateTime(PInt64(FArgs[aIndex])^);
          varDate:
              PDateTime(AValue.VPointer)^ := TMonoConvert.Int64ToDateTime(PInt64(FArgs[aIndex])^);
        end;
      end;
    end;
  end;
end;

procedure TMonoInvoke.SetVarRec(aIndex: integer; aValue: TVarRec);
begin
  if (aValue.VType = vtPointer)and(mono_type_is_byref(FArgTypes[aIndex].MonoType)) then
  begin
    case mono_type_get_type(FArgTypes[aIndex].MonoType) of
      MONO_TYPE_I1:
         shortInt(aValue.VPointer^) := PShortInt(FArgs[aIndex])^;
      MONO_TYPE_U1:
         byte(aValue.VPointer^) := PByte(FArgs[aIndex])^;
      MONO_TYPE_I2:
         smallint(aValue.VPointer^) := PSmallInt(FArgs[aIndex])^;
      MONO_TYPE_U2:
         word(aValue.VPointer^) := PWord(FArgs[aIndex])^;
      MONO_TYPE_R4:
         single(aValue.VPointer^) := PSingle(FArgs[aIndex])^;
      MONO_TYPE_BOOLEAN:
          Boolean(aValue.VPointer^) := PWordBool(FArgs[aIndex])^;
      MONO_TYPE_CHAR:
          Char(aValue.VPointer^) := PChar(FArgs[aIndex])^;
      MONO_TYPE_STRING:
        begin
          string(aValue.VPointer^) := TMonoConvert.MonoStrToString(PMonoString(FArgs[aIndex]));
        end;
      MONO_TYPE_VALUETYPE:
      begin
        if (FArgTypes[aIndex].MonoType = TMonoConvert.DateTimeTypeRef) then
          TDateTime(aValue.VPointer^) := TMonoConvert.Int64ToDateTime(PInt64(FArgs[aIndex])^);
      end;
    end;
  end;
  //else if (aValue.VType = vtPointer)and(not mono_type_is_byref(FArgTypes[aIndex].MonoType)) then
 // begin
  //  FArgs[aIndex] := nil; // fix clear(): invalid pointer operation
  //end;
end;

{ TRecArgInfo }

constructor TRecArgInfo.Create(aMonoType: PMonoType; aDispose: boolean; aArgTyp: TRecArgTyp);
begin
  MonoType := aMonoType;
  _Dispose := aDispose;
  ArgTyp := aArgTyp;
end;

{ TAppDomain }

function TAppDomain.AddAssembly(aFileName: string): PMonoAssembly;
var
  lFileName: UTF8String;
begin
  lFileName := UTF8Encode(aFileName);
  result := mono_assembly_open(PAnsiChar(lFileName), nil);
end;

constructor TAppDomain.Create(aName: string; aMonoJit: TMonoJit; aInit: boolean = false);
var
  lName: UTF8String;
begin
  inherited Create;
  FInitDom := aInit;
  lName := UTF8Encode(aName);
  if aInit then
    Fdomain := mono_jit_init_version(PAnsichar(lName), PAnsiChar(aMonoJit.FVersion))
  else
    Fdomain := mono_domain_create_appdomain(PAnsiChar(lName), nil);

  FProxyNameSpace := DefaultProxyNameSpace;
  FProxyClassName := DefaultProxyClassName;
end;

destructor TAppDomain.Destroy;
begin
  if not FInitDom then
    mono_domain_unload(Fdomain);
  inherited;
end;

procedure TAppDomain.LoadMainAssembly(aFileName: string);
var
  lFileName: UTF8String;
begin
  lFileName := UTF8Encode(aFileName);
  Fassembly := mono_domain_assembly_open(Fdomain, PAnsiChar(lFileName));
  if Fassembly = nil then
    raise Exception.Create('assembly load error');
  FImage := mono_assembly_get_image(Fassembly);
end;

end.
